#!/usr/bin/env python
# encoding: utf-8

import random

class Criatura(object):	

    atributos = [ 'fuerza', 'inteligencia', 'contextura', 'destreza',
                  'carisma', 'sabiduria' ]

    puntos = ['hp', 'mp', 'xp']

    def __init__(self, estado, habilidades):
        self.__estado = dict(estado)
        self.__habilidades = dict(habilidades)
        self.nombre = ""

        self.__max = dict(estado)
        del self.__max["xp"]

    # Funciones obligatorias según especificación
    def obtener_habilidades(self):
        return dict(self.__habilidades)

    def obtener_estado(self):
        return dict(self.__estado)

    def _aplicar_consecuencias(self, consecuencias):
        """ Aplica las consecuencias recibidas al estado de la criatura. """
        for k in consecuencias:
            self.__estado[k] += consecuencias[k]
            if k in self.__max and self.__estado[k] > self.__max[k]:
                self.__estado[k] = self.__max[k]
            if self.__estado[k] < 0:
                self.__estado[k] = 0

    # Funciones adicionales
    def esta_viva(self):
        return self.__estado["hp"] > 0

    def obtener_max(self):
        return dict(self.__max)

    def descansar(self):
        if self.esta_viva():
            for atributo in self.__max:
                self.__estado[atributo] = self.__max[atributo]

    def velocidad(self):
        return self.__estado["contextura"] + self.__estado["destreza"]

    def verificar_costos(self, accion):
        if not accion in self.__habilidades:
            return False

        costos = self.__habilidades[accion].obtener_costos()
        for clave, valor in costos.iteritems():
            if not clave in self.__estado or self.__estado[clave] < valor:
                return False

        return True

    # Sobrecarga de operadores
    def __str__(self):
        salida = "%s " % self.nombre
        for valor in self.atributos+self.puntos:
            salida += "%s: %d " % (valor[:3], self.__estado[valor])
        salida += "Hab: " + str(self.__habilidades.keys())
        return salida
    
    def __repr__(self):
        return str(self)

    def __hash__(self):
        return hash(str(self))


import csv, time
class Juego(object):
   
    def __init__(self):
        self.puntajes = {}
        self.leer_habilidades()
        self.leer_jugadores()

    def leer_habilidades(self):
        """ Carga las habilidades que están listadas en el archivo 
            de habilidades. """

        self.habilidades = {}

        arch_hab = open("habilidades.txt")
        csv_hab = csv.reader(arch_hab)
        for modulo, clase in csv_hab:
            modulo = __import__(modulo, fromlist=["habilidades"])
            clase = getattr(modulo, clase)
            self.habilidades[clase().nombre] = clase
        arch_hab.close()

    def leer_jugadores(self):

        self.jugadores = []
        arch_jug = open("jugadores.txt")
        csv_jug = csv.reader(arch_jug)
        for modulo, clase in csv_jug:
            modulo = __import__(modulo, fromlist=["jugadores"])
            clase = getattr(modulo, clase)
            self.jugadores.append(clase())
        arch_jug.close()


    def dados(self, caras, cantidad):
        return [random.randrange(1,caras+1) for d in xrange(cantidad)]

    # Este codigo está repetido por especificación, no está bien
    def habilidad_satisface_costos(self, estado, habilidad):
        costos = habilidad.obtener_costos()
        for clave, valor in costos.iteritems():
            if not clave in estado or estado[clave] < valor:
                return False

        return True

    def crear_criatura(self):

        # Cálculo de los valores iniciales
        estado = {'xp': 0}
        for a in Criatura.atributos:
            estado[a] = sum(self.dados(6,3))
        estado['hp'] = sum(estado.values())
        estado['mp'] = estado['inteligencia'] + estado['sabiduria']

        # Elección de las habilidades iniciales
        habilidades = {}
        keys = self.habilidades.keys()
        while len(habilidades) < 3:
            # Elige una habilidad al azar
            elegida = random.choice(keys)
            # verifica que no esté repetida
            if elegida in habilidades:
                continue
            # verificar que estén dados los requerimientos
            habilidad = self.habilidades[elegida]()
            if not self.habilidad_satisface_costos(estado, habilidad):
                continue
            # guarda la habilidad en el diccionario
            habilidades[elegida] = habilidad

        return Criatura (estado, habilidades)

    def elegir_y_crear(self, jugador):
        criatura = jugador.elegir_criatura()
        if not criatura:
            criatura = self.crear_criatura()
            jugador.agregar_criatura(criatura)
        return criatura

    def combate(self, jugador1, jugador2, cantidad=1):
        """ Realiza una cantidad de combates entre dos jugadores. """

        print "\nCombate entre %s y %s" % (jugador1.nombre, jugador2.nombre)
        resultado = {jugador1.nombre: 0, jugador2.nombre: 0}
        for x in xrange(cantidad):
            ganador = self._combate(jugador1, jugador2)
            if ganador: resultado[ganador.nombre] += 1

        print "Resultado: %s: %d puntos - %s: %d puntos\n" % (
            jugador1.nombre, resultado[jugador1.nombre],
            jugador2.nombre, resultado[jugador2.nombre])
        #time.sleep(1)

        if resultado[jugador1.nombre] > resultado[jugador2.nombre]:
            return jugador1
        elif resultado[jugador2.nombre] > resultado[jugador1.nombre]:
            return jugador2
        else:
            return None


    def _combate(self, jugador1, jugador2, debug=False, max_turnos = 100):
        """ Realiza el combate completo entre dos jugadores. """

        # Cada jugador elige la criatura con la que quiere pelear
        criatura1 = self.elegir_y_crear(jugador1)
        criatura2 = self.elegir_y_crear(jugador2)

        atacante = (jugador1, criatura1, jugador2, criatura2)
        oponente = (jugador2, criatura2, jugador1, criatura1)

        # Mientras no haya un ganador del combate
        ganador = False; contador = max_turnos
        while not ganador:

            # Definición del orden
            orden = [atacante, oponente]
            if criatura1.velocidad() < criatura2.velocidad():
                orden = [oponente, atacante]

            # Ataque de cada jugador/criatura
            for jugador, criatura, enemigo, enemiga in orden:

                accion, destino = jugador.elegir_accion(criatura, enemiga)
                if debug: print "%s eligió %s " % (jugador.nombre, accion)

                # Permite huir sin que muera la criatura
                if accion == "huir":
                    ganador = enemigo
                    break

                if not criatura.verificar_costos(accion):
                    # Si no puede hacer la acción, pierde el turno
                    continue

                accion = criatura.obtener_habilidades()[accion]
                cambios = accion.obtener_consecuencias(criatura, destino)
                criatura._aplicar_consecuencias(cambios[0])
                destino._aplicar_consecuencias(cambios[1])

                if not (criatura.esta_viva() and enemiga.esta_viva()):
                    break
            
            contador -= 1
            if contador <= 0:
                break

            if debug: 
                print "Fin del turno:\n%s:%s\n%s:%s" % (jugador1.nombre, criatura1,jugador2.nombre, criatura2)
                raw_input("")
            # Al final de cada turno, si una de las dos murio, 
            # el contrario es el ganador
            if criatura1.esta_viva() and not criatura2.esta_viva():
                ganador = jugador1
                jugador2.eliminar_criatura(criatura2)
                criatura1._aplicar_consecuencias({'xp': 1})
            elif criatura2.esta_viva() and not criatura1.esta_viva():
                ganador = jugador2
                jugador1.eliminar_criatura(criatura1)
                criatura2._aplicar_consecuencias({'xp': 1})

        # Al final del combate, se suman los puntos y las criaturas descansan
        if ganador:
            self.puntajes[ganador.nombre] = self.puntajes.get(ganador.nombre,0)+1

        # Las dos descansan.  Si una de las dos está muerta, no pasa nada
        criatura1.descansar()
        criatura2.descansar()

        return ganador

    def torneo_todos_contra_todos(self, peleas):

        print "\nIniciando torneo todos contra todos\n"
        time.sleep(1)
        random.shuffle(self.jugadores)
        for i, jugador1 in enumerate(self.jugadores[1:]):
            for j, jugador2 in enumerate(self.jugadores[:i+1]):
                self.combate(jugador1, jugador2, peleas)

        print "\nPuntajes finales:\n"
        jugadores = self.puntajes.items()
        jugadores.sort(key=lambda x: x[1])
        for jugador, puntos in jugadores:
            print "%30s: %d puntos" % (jugador, puntos)
        time.sleep(1)


    def torneo_llave(self, peleas=1000):
        print "\nIniciando torneo con eliminatorias.\n"
        time.sleep(1)
        cola = list(self.jugadores)
        random.shuffle(cola)
        while len(cola) > 1:
            jugador1 = cola.pop(0)
            jugador2 = cola.pop(0)
            ganador = self.combate(jugador1, jugador2, peleas)
            if ganador: cola.append(ganador)
        print "\nGanador del torneo: %s" % cola[0].nombre
        

if __name__ == "__main__":

    import sys

    cant = 1000
    if len(sys.argv) > 1:
        cant = sys.argv[1]

    juego = Juego()
    juego.torneo_todos_contra_todos(cant)
    raw_input("\n\nPresione ENTER para continuar\n")
    juego.torneo_llave(cant)




# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
